#include "testmainwindow.h"
#include "ui_testmainwindow.h"

#include <QSettings>
#include <QFileDialog>
#include <QMessageBox>
#include <QShortcut>

#include "mp3pcmbuffer.h"
#include "pcmgenerator.h"
#include "shredmateengine.h"
#include "marker.h"
#include "range.h"
#include "timeline.h"

#include <QDebug>

TestMainWindow::TestMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::TestMainWindow),
    m_timeline(new Timeline(this)),
    m_pcmBuffer(NULL),
    m_engine(new ShredmateEngine(this))
{
    ui->setupUi(this);
    ui->timeline->setScene(m_timeline);

    QSettings settings;
    restoreGeometry(settings.value("mainWindowGeometry").toByteArray());
    restoreState(settings.value("mainWindowState").toByteArray());

    connect(m_engine, SIGNAL(notify()), SLOT(handleNotify()));

    connect(m_timeline, SIGNAL(adjusted(qint64)), this, SLOT(handleAdjustedManually(qint64)));
    connect(m_timeline, SIGNAL(selectedMarkersChanged()), this, SLOT(handleSelectedMarkersChanged()));
    connect(m_timeline, SIGNAL(focusedMarkerChanged()), this, SLOT(handleFocusedMarkerChanged()));

    connect(m_timeline, SIGNAL(markerAdjusted(Marker)), this, SLOT(updateRanges()));
    new QShortcut(Qt::Key_Z, this, SLOT(focusPreviousMarker()));
    new QShortcut(Qt::Key_X, this, SLOT(focusNextMarker()));
}

TestMainWindow::~TestMainWindow()
{
    delete ui;
}

void TestMainWindow::closeEvent(QCloseEvent* event)
{
    Q_UNUSED(event)

    QSettings settings;
    settings.setValue("mainWindowGeometry", saveGeometry());
    settings.setValue("mainWindowState", saveState());

    if (m_pcmBuffer)
        storeEngineState();
}

void TestMainWindow::on_pushButtonOpen_clicked()
{
    QSettings settings;
    bool wasPlaying = m_engine->isPlaying();
    if (wasPlaying)
        m_engine->stop();

    QString selectedFile = QFileDialog::getOpenFileName(this, tr("Open audio file"), settings.value("lastOpenedDirectory").toString(), tr("Audio files (*.mp3)"));

    if (!selectedFile.isEmpty())
    {
        if (m_pcmBuffer)
            storeEngineState();

        ui->labelFileName->setText(tr("Opening and decoding..."));
        ui->labelFileName->repaint();

        settings.setValue("lastOpenedDirectory", QFileInfo(selectedFile).absolutePath());

        m_engine->setPCMBuffer(NULL);

        delete m_pcmBuffer;
        m_pcmBuffer = new MP3PCMBuffer(selectedFile);
        m_pcmBuffer->open();

        m_engine->setPCMBuffer(m_pcmBuffer);
        restoreEngineState();

        ui->labelFileName->setText(selectedFile);

        m_timeline->setLength(m_engine->bufferLength());
        m_timeline->setPosition(m_engine->position());

        updateMarkers();
        updateRanges();
    }
    else
    {
        if (wasPlaying)
            m_engine->play();
    }
}

void TestMainWindow::on_pushButtonPlay_clicked()
{
    m_engine->play();
}

void TestMainWindow::on_pushButtonStop_clicked()
{
    m_engine->stop();
}

void TestMainWindow::on_pushButtonTogglePause_clicked()
{
    m_engine->togglePause();
}

void TestMainWindow::handleNotify()
{
    m_timeline->setPosition(m_engine->position());
}

void TestMainWindow::handleAdjustedManually(qint64 position)
{
    m_engine->setPosition(position / 2 * 2);
    updateActiveRange();
}

void TestMainWindow::handleSelectedMarkersChanged()
{
    ui->pushButtonRemoveMarker->setEnabled(m_timeline->selectedMarkers().count() > 0);
    ui->pushButtonReplaceMarker->setEnabled(m_timeline->selectedMarkers().count() > 0);
    ui->pushButtonAddRange->setEnabled(m_timeline->selectedMarkers().count() == 2);
    ui->pushButtonAddRangeAndSetActive->setEnabled(m_timeline->selectedMarkers().count() == 2);
}

void TestMainWindow::handleFocusedMarkerChanged()
{
    qDebug() << "handleFocusedMarkerChanged";
    ui->pushButtonSeekToMarker->setEnabled(m_timeline->focusedMarker().isValid());
}

void TestMainWindow::on_checkBoxLooping_toggled(bool checked)
{
    m_engine->setLooping(checked);
}

void TestMainWindow::on_pushButtonAddMarker_clicked()
{
    QList<Marker> markers = m_engine->markers();
    Marker newMarker = Marker(m_engine->position(), QString::number(m_engine->position()));
    markers.append(newMarker);
    m_engine->setMarkers(markers);
    updateMarkers();
    m_timeline->setFocusedMarker(newMarker);
}

void TestMainWindow::updateMarkers()
{
    m_timeline->setMarkers(m_engine->markers());

    updateRanges();
}

void TestMainWindow::on_pushButtonRemoveMarker_clicked()
{
    if (m_timeline->selectedMarkers().count() == 0)
        return;

    QList<int> removedIndexes;
    foreach (Marker marker, m_timeline->selectedMarkers())
    {
        int index = m_engine->markers().indexOf(marker);
        if (index >= 0)
            removedIndexes.append(index);
    }

    qSort(removedIndexes.begin(), removedIndexes.end(), qGreater<int>());
    QList<Marker> markers = m_engine->markers();
    foreach (int index, removedIndexes)
        markers.removeAt(index);

    m_engine->setMarkers(markers);
    updateMarkers();

}

void TestMainWindow::on_pushButtonSeekToMarker_clicked()
{
    Marker marker = m_timeline->focusedMarker();
    if (marker.isValid())
    {
        m_engine->setPosition(marker.position());
        handleNotify();
        updateActiveRange();
    }
}

void TestMainWindow::on_pushButtonAddRange_clicked()
{
    Marker marker1 = m_timeline->selectedMarkers()[0];
    Marker marker2 = m_timeline->selectedMarkers()[1];

    QList<Range> ranges = m_engine->ranges();
    ranges.append(Range(marker1, marker2));
    m_engine->setRanges(ranges);
    updateRanges();
}

void TestMainWindow::updateRanges()
{
    ui->listWidgetRanges->clear();
    foreach(Range range, m_engine->ranges())
    {
        QString name = QString("%1 (%2) -> %3 (%4)")
                .arg(range.lowerMarker().name())
                .arg(range.lowerMarker().position())
                .arg(range.upperMarker().name())
                .arg(range.upperMarker().position());
        range.setName(name);
        QListWidgetItem* item = new QListWidgetItem(range.name());
        item->setFlags (item->flags() | Qt::ItemIsEditable);
        ui->listWidgetRanges->addItem(item);
    }
    updateActiveRange();
}

void TestMainWindow::focusPreviousMarker()
{
    m_timeline->focusPreviousMarker();
}

void TestMainWindow::focusNextMarker()
{
    m_timeline->focusNextMarker();
}

void TestMainWindow::updateActiveRange()
{
    if (m_engine->activeRange() != -1)
    {
        Range activeRange = m_engine->ranges()[m_engine->activeRange()];
        ui->labelActiveRange->setText(activeRange.name());
        m_timeline->setActiveRange(activeRange);
    }
    else
    {
        ui->labelActiveRange->setText(tr("-"));
        m_timeline->setActiveRange(Range());
    }
}

void TestMainWindow::on_pushButtonSetAsActiveRange_clicked()
{
    QListWidgetItem* item = ui->listWidgetRanges->selectedItems()[0];
    int index = ui->listWidgetRanges->row(item);
    m_engine->setActiveRange(index);
    updateActiveRange();
}

void TestMainWindow::on_listWidgetRanges_itemSelectionChanged()
{
    ui->pushButtonSetAsActiveRange->setEnabled(ui->listWidgetRanges->selectedItems().count() == 1);
    ui->pushButtonRemoveRange->setEnabled(ui->listWidgetRanges->selectedItems().count() > 0);
}

void TestMainWindow::on_pushButtonRemoveRange_clicked()
{
    QList<Range> ranges = m_engine->ranges();
    foreach (QListWidgetItem* item, ui->listWidgetRanges->selectedItems())
    {
        int index = ui->listWidgetRanges->row(item);
        if (index >= 0)
        {
            ranges.removeAt(index);
        }
    }
    m_engine->setRanges(ranges);
    updateRanges();
}

void TestMainWindow::on_pushButtonAddRangeAndSetActive_clicked()
{
    on_pushButtonAddRange_clicked();
    ui->listWidgetRanges->setCurrentRow(ui->listWidgetRanges->count() - 1);
    on_pushButtonSetAsActiveRange_clicked();
}

void TestMainWindow::on_pushButtonResetActiveRange_clicked()
{
    m_engine->setActiveRange(-1);
    updateRanges();
}

void TestMainWindow::on_pushButtonReplaceMarker_clicked()
{
    on_pushButtonRemoveMarker_clicked();
    on_pushButtonAddMarker_clicked();
}

namespace
{
    QString dataPath()
    {
        return QDir::homePath() + QDir::separator() + ".shredmate";
    }

    QString dataFile(QByteArray hash)
    {
        return dataPath() + QDir::separator() + hash.toHex() + ".dat";
    }
}

void TestMainWindow::storeEngineState()
{
    QDir dir(dataPath());
    if (!dir.exists())
        dir.mkpath(dataPath());

    QFile file(dataFile(m_pcmBuffer->hash()));
    file.open(QIODevice::WriteOnly);

    QDataStream stream(&file);
    stream.setVersion(QDataStream::Qt_4_7);
    stream << DataStreamVersion << m_engine->markers() << m_engine->ranges();
}

void TestMainWindow::restoreEngineState()
{
    int version = 0;
    QList<Marker> markers;
    QList<Range> ranges;

    QFile file(dataFile(m_pcmBuffer->hash()));

    file.open(QIODevice::ReadOnly);

    if (file.error() == QFile::NoError)
    {
        QDataStream stream(&file);
        stream.setVersion(QDataStream::Qt_4_7);
        stream >> version;
        if (version == DataStreamVersion)
            stream >> markers >> ranges;
    }

    m_engine->setMarkers(markers);

    QList<Range> realRanges;
    foreach(Range range, ranges)
    {
        int index1 = markers.indexOf(range.marker1());
        int index2 = markers.indexOf(range.marker2());

        if (index1 != -1 && index2 != -1)
            realRanges.append(Range(markers[index1], markers[index2], range.name()));
    }

    m_engine->setRanges(realRanges);
}
